import hashlib
import hmac
import requests
import json
import datetime
import urllib.parse
import base64
import sys

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

def to_milliseconds(time:datetime.datetime):
  return int(time.timestamp()) * 1000

class Type(Enum):
  BUY_MARKET = "buy-market"
  SELL_MARKET = "sell-market"
  BUY_LIMIT = "buy-limit"
  SELL_LIMIT = "sell-limit"
  BUY_IOC = "buy-ioc"
  SELL_IOC = "sell-ioc"
  BUY_LIMIT_MAKER = "buy-limit-maker"
  SELL_LIMIT_MAKER = "sell-limit-maker"
  BUY_STOP_LIMIT = "buy-stop-limit"
  SELL_STOP_LIMIT = "sell-stop-limit"

class Direction(Enum):
  BUY = "buy"
  SELL = "sell"

class OrderPriceType(Enum):
  LIMIT = "limit"
  OPPONENT = "opponent"
  POST_ONLY = "post_only"
  IOC = "ioc"
  FOK = "fok"
  OPPONENT_IOC = "opponent_ioc"
  OPPONENT_FOK = "opponent_fok"

class Offset(Enum):
  OPEN = "open"
  CLOSE = "close"

class HuobiAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _get_signed_params(self, api_host, method, endpoint, params=None):
    assert method in ('GET', 'POST'), method
    timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
    if api_host == 'xiguaapi.hbdm.com':
      api_host = 'api.hbdm.com'
    full_params = {
        'AccessKeyId': self._key.access_key,
        'SignatureMethod': 'HmacSHA256',
        'SignatureVersion': '2',
        'Timestamp': timestamp
    }
    if method == 'GET':
      full_params.update(params)

    sorted_params = sorted(full_params.items(), key=lambda d: d[0])
    encode_params = urllib.parse.urlencode(sorted_params)
    payload = [method, api_host, endpoint, encode_params]
    payload = '\n'.join(payload).encode()
    signature = hmac.new(self._key.secret_key_bytes,
                         payload, digestmod=hashlib.sha256)
    signature = base64.b64encode(signature.digest()).decode()
    full_params['Signature'] = signature
    return full_params

  def __call__(self, req: requests.PreparedRequest):
    assert req.method in ('GET', 'POST'), req.method
    method = req.method
    parsed_result = urllib.parse.urlparse(req.url)
    api_host = parsed_result.netloc
    path = parsed_result.path
    url = urllib.parse.ParseResult(parsed_result.scheme,
                                   parsed_result.netloc,
                                   parsed_result.path,
                                   '',
                                   '',
                                   '').geturl()

    if method == 'GET':
      params = urllib.parse.parse_qsl(parsed_result.query)
      params = self._get_signed_params(api_host, req.method, path, params)
      req.prepare_url(url, params)
    else:
      params = self._get_signed_params(api_host, req.method, path)
    req.prepare_url(url, params)
    return req


class HuobiPrivateClient():
  def __init__(self, key, mea, ip=None):
    self._mea = mea
    if mea == "Spot.Huobi.v1":
      self._url = "https://api.huobi.pro"
    else:
      self._url = "https://api.hbdm.com"

    self._auth = HuobiAuth(key)
    self._query = SyncQueryer(api_host=self._url, auth=self._auth, ip=ip)

  def __check_spot(self):
    assert (self._mea == "Spot.Huobi.v1")

  def get_open_order(self, timeout=None):
    method = "GET"
    if self._mea == "Spot.Huobi.v1":
      path = "/v1/order/openOrders"
    else:
      assert (0)

    params = {"size": 500}
    response = self._query.query(
        method=method, path=path, params=params, timeout=timeout)
    return response

  def get_history_order(self, timeout=None):
    method = "GET"
    if self._mea == "Spot.Huobi.v1":
      path = "/v1/order/history"
    else:
      assert (0)

    params = {"size": 500}
    response = self._query.query(
        method=method, path=path, params=params, timeout=timeout)
    return response

  def get_deposit(self):
    self.__check_spot()
    method = "GET"
    path = "/v1/query/deposit-withdraw"
    params = {"type": "deposit"}
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def accounts_info(self):
    self.__check_spot()
    method = "GET"
    path = "/v1/account/accounts"
    response = self._query.query(
        method=method, path=path)
    return response

  def account_ledger(self):
    self.__check_spot()
    method = "GET"
    path = "/v2/account/ledger"
    params = {"accountId": 33567196}
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def query_deposit(self):
    self.__check_spot()
    method = "GET"
    path = "/v2/account/ledger"
    path = "/v1/query/deposit-withdraw"

    params = {"type": "deposit", "size": 500}
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def query_withdraw(self):
    self.__check_spot()
    method = "GET"
    path = "/v1/query/deposit-withdraw"

    params = {"type": "withdraw", "size": 500}
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def get_fill_history(
    self,
    symbol: str=None,
    types: Type=None,
    start_time: int=None,
    end_time: int=None,
    from_id: str=None,
    direct: str=None,
    size: int=None,
  ):
    self.__check_spot()
    method = "GET"
    path = "/v1/order/matchresults"

    params = {
      "symbol": symbol,
      "types": types and types.value,
      "start-time": start_time,
      "end-time": end_time,
      "from": from_id,
      "direct": direct,
      "size": size,
    }
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def submit_order(
      self,
      contract_code: str,
      volume: str,
      direction: Direction,
      lever_rate: str,
      order_price_type: OrderPriceType,
      offset: Offset = None,
      price: str = None,
      client_order_id: str = None,
      timeout=None
    ):
    assert contract_code, "contract_code must be not None and not empty!"
    assert volume, "volume must be not None and not empty!"
    assert direction, "direction must be not None!"
    assert lever_rate, "lever_rate must be not None and not empty!"
    assert order_price_type, "order_price_type must be not None!"

    method = "POST"
    if self._mea == "Futures.Huobi.v1-linear-swap":
      path = "/linear-swap-api/v1/swap_cross_order"
    else:
      assert (0)

    json = {
      "contract_code": contract_code,
      "volume": volume,
      "direction": direction.value,
      "lever_rate": lever_rate,
      "order_price_type": order_price_type.value,
      "offset": offset and offset.value,
      "price": price,
      "client_order_id": client_order_id,
    }
    response = self._query.query(
        method=method, path=path, json=json, timeout=timeout)
    return response
  
  def get_balance(self, timeout=None):
    if self._mea == "Futures.Huobi.v1-linear-swap":
      method = "POST"
      path = "/linear-swap-api/v1/swap_cross_account_info"
      json = {
      }
    elif self._mea == "Spot.Huobi.v1":
      method = "GET"
      resp = self.accounts_info()
      path = f"/v1/account/accounts/{resp.json()['data'][0]['id']}/balance"
      json = None
    else:
      assert (0)

    response = self._query.query(
        method=method, path=path, json=json, timeout=timeout)
    return response

  def get_order_fill_range(self, begin:str, end:str, native_symbol:str):
    end = datetime.datetime.strptime(end, "%Y%m%d")
    begin = datetime.datetime.strptime(begin, "%Y%m%d")

    fill_list = []
    while end >= begin:
      end_time = end
      begin_time = end - datetime.timedelta(hours=3)

      info = self.get_fill_history(symbol=native_symbol,  start_time=to_milliseconds(begin_time), end_time=to_milliseconds(end_time), size=500)
      info = info.json()["data"]
      size = len(info)
      print(end_time, "   ", begin_time, "  ", size)
      if size == 500:
        raise

      fill_list = fill_list + info
      end = begin_time

    return fill_list


if __name__ == "__main__":
  key_path = sys.argv[1]
  config = {}
  config["key_path"] = key_path
  config["mea"] = "Spot.Huobi.v1"
  huobi = HuobiPrivateClient(config["key_path"], config["mea"])
  resp = huobi.get_order_fill_range("20230510", "20230701", "mbxusdt")
  print(resp)
  # with open("./prestoinvt23.json", "w+") as f:
  #   json.dump(resp, f, indent=2)
